library(ggplot2)
library(readr)
library(dplyr)
library(tidyr)
library(forcats)
library(DT)
library(tibble)
library(cowplot)

theme_set(theme_bw() + 
            theme(legend.title = element_blank(),
                  panel.grid.minor = element_blank()))

Read in files

and convert to wide format

#4CE long format Labs Data
patient_obs <- read_csv('penn-data/labs_long_thrombo_v2.csv') %>% 
  mutate(severity = (severe_ind == 1) %>% 
           as_factor() %>% 
           fct_recode('nonsevere' = 'FALSE', 'severe' = 'TRUE'))

#Code, Descriptions and Ranges
lab_mapping <- read_csv('public-data/loinc-map.csv')
load('public-data/lab.range.rda')
# load('public-data/code.dict.rda')
lab_bounds <- lab.range[, -1] %>% 
  colMeans() %>% 
  data.frame(value = .) %>% 
  rownames_to_column() %>% 
  separate(col = rowname, into = c('LOINC', 'bound'), sep = '_') %>% 
  mutate(LOINC = gsub('loinc', '', LOINC) %>% gsub('\\.', '-', .),
         value = exp(value)) %>% 
  pivot_wider(names_from = 'bound', values_from = 'value') %>%
  left_join(lab_mapping, by = 'LOINC') %>%
  {.}
patient_obs_wide <- patient_obs %>% 
  left_join(lab_bounds, by = c('concept_code' = 'LOINC')) %>% 
  select(- concept_code) %>% 
  pivot_wider(id_cols = c(patient_num, days_since_admission, severity),
              names_from = short_name,
              values_from = value,
              values_fn = mean)

#check NAs in the Wide format
na_stats <- patient_obs_wide %>% 
  select(- c(patient_num, days_since_admission, severity)) %>% 
  is.na() %>% 
  `!` 

na_df <- data.frame(value_existed = colSums(na_stats), 
           prop_existed = colMeans(na_stats)) %>% 
  rownames_to_column('lab') %>% 
  mutate(prop_na = 1 - prop_existed,
         lab = fct_reorder(lab, value_existed))

n_values <- na_df %>% 
  ggplot(aes(x = value_existed, y = lab)) +
  geom_col() + 
  labs(x = 'Number of values', y = NULL)

na_prob <- na_df %>%
  rename('Valid value' = prop_existed, 'NA' = prop_na) %>% 
  pivot_longer(c(`Valid value`, `NA`)) %>% 
  ggplot(aes(x = value, y = lab, fill = name)) +
  geom_col() + 
  scale_fill_discrete(guide = guide_legend(reverse = TRUE)) +
  labs(x = 'Proportion', y = NULL) +
  # guides(fill = guide_legend(reverse = TRUE))
  theme(
    axis.text.y = element_blank(),
    legend.key.width = unit(6, 'mm'),
    legend.key.height = unit(4, 'mm'),
    legend.position = 'bottom')

plot_grid(n_values, na_prob, nrow = 1, axis = 'b', align = 'h')

penn_na_df <- na_df

Number of observation (days) per patient

days_count_min_max <- patient_obs_wide %>%
  group_by(patient_num, severity) %>%
  summarise(
    n_values = n_distinct(days_since_admission),
    min_day = min(days_since_admission),
    max_day = max(days_since_admission),
    .groups = 'drop'
  ) %>% 
  mutate(time_obs = max_day - min_day) %>% 
  select(-patient_num) %>% 
  add_count(severity, name = 'n_severity') 

agg_n_values <- days_count_min_max %>% 
  count(severity, n_severity, n_values,
        name = 'n_nvals')
agg_max_day <- days_count_min_max %>% 
  count(severity, n_severity, max_day,
        name = 'n_maxday')

(n_severe <- sum(days_count_min_max$severity == 'severe'))
## [1] 817
(n_nonsevere <- sum(days_count_min_max$severity == 'nonsevere'))
## [1] 1876
# summary(days_count_min_max$n_values)

Histogram of the number of days with at least one observation

agg_n_values %>% 
  ggplot(aes(x = n_values, y = n_nvals, fill = severity)) +
  geom_col(alpha = 0.5) +
  scale_fill_brewer(palette = 'Dark2', direction = -1) +
  labs(fill = 'Severe?', 
       x = "Number of days with data", 
       y = "Count")

Histogram of length of stay

i.e. last day with observation-first day with observation

We need to check for readmission here.

agg_max_day %>% 
  ggplot(aes(x = max_day, y = n_maxday, fill = severity)) +
  geom_col(alpha = 0.5) +
  scale_fill_brewer(palette = 'Dark2', direction = -1) +
  labs(fill = 'Severe?', 
       x = "Number of days with data", 
       y = "Count")

Analyze missingness and frequency of measures for each lab

patient_obs_long <- patient_obs_wide %>% 
  pivot_longer(-c(patient_num, days_since_admission, severity),
               names_to = 'lab', values_to = 'value',
               values_drop_na = TRUE)
  
per_lab <- patient_obs_long %>% 
  group_by(lab, patient_num, severity) %>% 
  count(name = 'n_obs') %>% 
  ungroup() %>% 
  group_by(lab, severity) %>% 
  count(n_obs) %>% 
  ungroup() %>% 
  pivot_wider(names_from = severity, values_from = n, values_fill = 0) %>% 
  mutate(both_severities = nonsevere + severe) %>% 
  mutate(prop_nonsevere = nonsevere/n_nonsevere,
         prop_severe = severe/n_severe,
         prop_both = both_severities/nrow(days_count_min_max))

lab_medians <-
  patient_obs_long %>% 
  add_count(lab, name = 'total_obs') %>% 
  group_by(lab) %>% 
  mutate(total_patients = length(unique(patient_num))) %>% 
  add_count(patient_num, name = 'n_obs_patients') %>% 
  mutate(median_obs_per_patient = median(n_obs_patients),
         n_greater0 = n_obs_patients > median_obs_per_patient,
         n_greater1 = n_obs_patients > median_obs_per_patient + 1,
         n_greater2 = n_obs_patients > median_obs_per_patient + 2) %>% 
  group_by(severity) %>% 
  mutate(each_med_obs_per_patient = median(n_obs_patients)) %>% 
  ungroup(severity) %>% 
  select(- c(days_since_admission, value)) %>% 
  distinct() %>% 
  group_by(lab) %>% 
  mutate(across(contains('n_greater'), sum)) %>% 
  select(- c(n_obs_patients, patient_num)) %>% 
  distinct() %>% 
  pivot_wider(names_from = severity, values_from = each_med_obs_per_patient) %>% 
  rename('median_obs_per_severe_patient' = severe,
         'median_obs_per_non_severe_patient' = nonsevere) %>% 
  select(lab, total_obs, total_patients, starts_with('med'), starts_with('n_'))

lab_medians %>% 
  datatable(rownames = FALSE)

n_greater0 shows the number of patients who had more observations than the median. n_greater1 shows the number of patients who had more observations than the median + 1. n_greater2 shows the number of patients who had more observations than the median + 2.

In the figure below:

  • Grey dash line: Reference Low
  • Grey solid line: Reference High
  • Black dash line: lower bound outlier (QC)
  • Black solid line: upper bound outlier (QC)
patient_obs_long %>% 
  left_join(lab_bounds, by = c('lab' = 'short_name')) %>% 
  ggplot(aes(y = severity, x = value, fill = severity)) +
  geom_violin() +
  scale_fill_brewer(palette = 'Dark2', guide = guide_legend(reverse = TRUE)) +
  labs(y = NULL, x = NULL) +
  geom_vline(aes(xintercept = `Reference Low`), linetype = 'dashed', color = 'grey') +
  geom_vline(aes(xintercept = `Reference High`), color = 'grey') +
  geom_vline(aes(xintercept = LB), linetype = 'dashed') +
  geom_vline(aes(xintercept = UB)) +
  facet_wrap(~ lab, scales = 'free', ncol = 2, strip.position = 'left') +
  theme(axis.text.y = element_blank())

Missing data heatmap

Capped at 20 days with observations.

per_lab %>% 
  select(lab, n_obs, both_severities, severe, nonsevere) %>% 
  filter(n_obs <= 20) %>% 
  pivot_longer(c(both_severities, severe, nonsevere)) %>% 
  mutate(name = name %>% fct_recode(
    'All patients' = 'both_severities',
    'Severe patients' = 'severe',
    'Non-severe patients' = 'nonsevere'
  )) %>% 
  ggplot(aes(x = n_obs, fill = value, y = fct_reorder(lab, n_obs))) + 
  geom_tile(colour = "white", size = 0.2)+
  geom_text(aes(label = value), colour = "white", size = 2) +
  scale_y_discrete(expand = c(0, 0))+
  scale_x_continuous(expand = c(0, 0),
                     breaks = c(1:max(per_lab$n_obs)),
                     labels = c(1:max(per_lab$n_obs)))+
  scale_fill_gradient(low = "lightgrey", high = "darkblue",
                      limits = c(0, max(per_lab$both_severities))) +
  facet_wrap(~ name, nrow = 1) +
  labs(x = 'Number of values a patient has for each lab',
       y = NULL, fill = '# patients') +
  theme(panel.grid.major = element_blank(),
        legend.position = c(0.93, 0.2),
        axis.ticks.y = element_blank()
  )

“Binned” heatmap: every 15 days

per_lab %>%
  mutate(obs_bin = cut(n_obs, breaks = 15, include.lowest = TRUE)) %>% 
  group_by(lab, obs_bin) %>% 
  summarise(both_severities = sum(both_severities), 
            severe = sum(severe), 
            nonsevere = sum(nonsevere), 
            .groups = 'drop') %>% 
  select(lab, obs_bin, both_severities, severe, nonsevere) %>% 
  pivot_longer(c(both_severities, severe, nonsevere)) %>% 
  mutate(name = name %>% fct_recode(
    'All patients' = 'both_severities',
    'Severe patients' = 'severe',
    'Non-severe patients' = 'nonsevere'
  )) %>% 
  ggplot(aes(x = obs_bin, fill = value, y = fct_reorder(lab, value))) + 
  geom_tile(colour = "white", size = 0.2) +
  geom_text(aes(label = value), colour = "white", size = 2) +
  scale_y_discrete(expand = c(0, 0))+
  scale_fill_gradient(low = "lightgrey", high = "darkblue") +
  facet_wrap(~ name, nrow = 1) +
  labs(x = 'Number of values a patient has for each lab',
       y = NULL, fill = '# patients') +
  theme(panel.grid.major = element_blank(),
        legend.position = c(0.93, 0.2),
        axis.text.x = element_text(angle = 90, hjust = 1),
        axis.ticks.y = element_blank()
  )

boxplot(per_lab$n_obs)

sum(per_lab$n_obs > 90)
## [1] 15
per_lab %>% 
  select(lab, n_obs, prop_both, prop_severe, prop_nonsevere) %>% 
  filter(n_obs <= 20) %>% 
  pivot_longer(c(prop_both, prop_severe, prop_nonsevere)) %>% 
  mutate(name = name %>% fct_recode(
    'Compared to all patients' = 'prop_both',
    'Compared to all severe patients' = 'prop_severe',
    'Compared to all non-severe patients' = 'prop_nonsevere'
  )) %>% 
  ggplot(aes(x = n_obs, fill = value, y = fct_reorder(lab, n_obs))) + 
  geom_tile(colour = "white", size = 0.2)+
  geom_text(aes(label = round(value, 2)), colour = "white", size = 2) +
  scale_y_discrete(expand = c(0, 0)) +
  scale_x_continuous(expand = c(0, 0),
                     breaks = c(1:max(per_lab$n_obs)),
                     labels = c(1:max(per_lab$n_obs)))+ 
  scale_fill_gradient(low = "lightgrey", high = "darkblue",
                      labels = scales::percent_format(accuracy = 1L)) +
  facet_wrap(~ name, nrow = 1) +
  labs(x = 'Number of values a patient has for each lab',
       y = NULL, fill = '% patients') +
  theme(panel.grid.major = element_blank(),
        legend.position = c(0.93, 0.2),
        axis.ticks.y = element_blank()
  )

per_lab %>%
  mutate(obs_bin = cut(n_obs, breaks = 15, include.lowest = TRUE)) %>% 
  group_by(lab, obs_bin) %>% 
  summarise(prop_both = sum(prop_both), 
            prop_severe = sum(prop_severe), 
            prop_nonsevere = sum(prop_nonsevere), 
            .groups = 'drop') %>% 
  select(lab, obs_bin, prop_both, prop_severe, prop_nonsevere) %>% 
  pivot_longer(c(prop_both, prop_severe, prop_nonsevere)) %>% 
  mutate(name = name %>% fct_recode(
    'Compared to all patients' = 'prop_both',
    'Compared to all severe patients' = 'prop_severe',
    'Compared to all non-severe patients' = 'prop_nonsevere'
  )) %>% 
  ggplot(aes(x = obs_bin, fill = value, y = fct_reorder(lab, value))) + 
  geom_tile(colour = "white", size = 0.2) +
  geom_text(aes(label = round(value, 2)), colour = "white", size = 2) +
  scale_y_discrete(expand = c(0, 0)) +
  scale_fill_gradient(low = "lightgrey", high = "darkblue",
                      labels = scales::percent_format(accuracy = 1L)) +
  facet_wrap(~ name, nrow = 1) +
  labs(x = 'Number of values a patient has for each lab',
       y = NULL, fill = '% patients') +
  theme(panel.grid.major = element_blank(),
        legend.position = c(0.93, 0.2),
        axis.text.x = element_text(angle = 90, hjust = 1),
        axis.ticks.y = element_blank()
  )

Denominator: total number of patients, total number of non-severe patients, and total number of severe patients, respectively.

per_lab %>% 
  select(lab, n_obs, severe, nonsevere) %>% 
  filter(n_obs <= 90) %>%
  pivot_longer(c(severe, nonsevere)) %>% 
  mutate(lab = fct_reorder(lab, n_obs),
         name = name %>% fct_recode(
    'Severe patients' = 'severe',
    'Non-severe patients' = 'nonsevere'
  )) %>% 
  ggplot(aes(x = n_obs, fill = name, y = value)) + 
  geom_col() +
  scale_fill_brewer(palette = 'Dark2', direction = -1) +
  facet_wrap(~ lab, scales = 'free') +
  labs(x = 'Number of values a patient has for each lab',
       y = NULL, fill = '# patients') +
  theme(panel.grid.major = element_blank(),
        legend.position = c(0.9, 0.2),
        axis.ticks.y = element_blank()
  )

sessionInfo()
## R version 4.0.3 (2020-10-10)
## Platform: x86_64-apple-darwin17.0 (64-bit)
## Running under: macOS Catalina 10.15.7
## 
## Matrix products: default
## BLAS:   /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRblas.dylib
## LAPACK: /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRlapack.dylib
## 
## locale:
## [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] cowplot_1.1.0 tibble_3.0.4  DT_0.16       forcats_0.5.0 tidyr_1.1.2  
## [6] dplyr_1.0.2   readr_1.4.0   ggplot2_3.3.2
## 
## loaded via a namespace (and not attached):
##  [1] RColorBrewer_1.1-2 pillar_1.4.6       compiler_4.0.3     tools_4.0.3       
##  [5] digest_0.6.27      jsonlite_1.7.1     evaluate_0.14      lifecycle_0.2.0   
##  [9] gtable_0.3.0       pkgconfig_2.0.3    rlang_0.4.8        rstudioapi_0.13   
## [13] cli_2.1.0          crosstalk_1.1.0.1  yaml_2.2.1         xfun_0.19         
## [17] withr_2.3.0        stringr_1.4.0      knitr_1.30         generics_0.1.0    
## [21] vctrs_0.3.4        htmlwidgets_1.5.2  hms_0.5.3          grid_4.0.3        
## [25] tidyselect_1.1.0   glue_1.4.2         R6_2.5.0           fansi_0.4.1       
## [29] rmarkdown_2.5      farver_2.0.3       purrr_0.3.4        magrittr_1.5      
## [33] scales_1.1.1       ellipsis_0.3.1     htmltools_0.5.0    assertthat_0.2.1  
## [37] colorspace_2.0-0   labeling_0.4.2     stringi_1.5.3      munsell_0.5.0     
## [41] crayon_1.3.4
LS0tCnRpdGxlOiAiTWlzc2luZ25lc3MgYW5hbHlzaXMiCmF1dGhvcjogQW1lbGlhIFRhbiwgQXJpYW5uYSBEYWdsaWF0aSwgVHJhbmcgTGUKZGF0ZTogIjI3IE9jdG9iZXIgMjAyMCIKLS0tCgoKYGBge3Igc2V0dXAsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShyZWFkcikKbGlicmFyeShkcGx5cikKbGlicmFyeSh0aWR5cikKbGlicmFyeShmb3JjYXRzKQpsaWJyYXJ5KERUKQpsaWJyYXJ5KHRpYmJsZSkKbGlicmFyeShjb3dwbG90KQoKdGhlbWVfc2V0KHRoZW1lX2J3KCkgKyAKICAgICAgICAgICAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKSkKYGBgCgojIyBSZWFkIGluIGZpbGVzIAphbmQgY29udmVydCB0byB3aWRlIGZvcm1hdApgYGB7UiBtZXNzYWdlPUZBTFNFfQojNENFIGxvbmcgZm9ybWF0IExhYnMgRGF0YQpwYXRpZW50X29icyA8LSByZWFkX2NzdigncGVubi1kYXRhL2xhYnNfbG9uZ190aHJvbWJvX3YyLmNzdicpICU+JSAKICBtdXRhdGUoc2V2ZXJpdHkgPSAoc2V2ZXJlX2luZCA9PSAxKSAlPiUgCiAgICAgICAgICAgYXNfZmFjdG9yKCkgJT4lIAogICAgICAgICAgIGZjdF9yZWNvZGUoJ25vbnNldmVyZScgPSAnRkFMU0UnLCAnc2V2ZXJlJyA9ICdUUlVFJykpCgojQ29kZSwgRGVzY3JpcHRpb25zIGFuZCBSYW5nZXMKbGFiX21hcHBpbmcgPC0gcmVhZF9jc3YoJ3B1YmxpYy1kYXRhL2xvaW5jLW1hcC5jc3YnKQpsb2FkKCdwdWJsaWMtZGF0YS9sYWIucmFuZ2UucmRhJykKIyBsb2FkKCdwdWJsaWMtZGF0YS9jb2RlLmRpY3QucmRhJykKYGBgCgpgYGB7cn0KbGFiX2JvdW5kcyA8LSBsYWIucmFuZ2VbLCAtMV0gJT4lIAogIGNvbE1lYW5zKCkgJT4lIAogIGRhdGEuZnJhbWUodmFsdWUgPSAuKSAlPiUgCiAgcm93bmFtZXNfdG9fY29sdW1uKCkgJT4lIAogIHNlcGFyYXRlKGNvbCA9IHJvd25hbWUsIGludG8gPSBjKCdMT0lOQycsICdib3VuZCcpLCBzZXAgPSAnXycpICU+JSAKICBtdXRhdGUoTE9JTkMgPSBnc3ViKCdsb2luYycsICcnLCBMT0lOQykgJT4lIGdzdWIoJ1xcLicsICctJywgLiksCiAgICAgICAgIHZhbHVlID0gZXhwKHZhbHVlKSkgJT4lIAogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSAnYm91bmQnLCB2YWx1ZXNfZnJvbSA9ICd2YWx1ZScpICU+JQogIGxlZnRfam9pbihsYWJfbWFwcGluZywgYnkgPSAnTE9JTkMnKSAlPiUKICB7Ln0KYGBgCgpgYGB7Un0KcGF0aWVudF9vYnNfd2lkZSA8LSBwYXRpZW50X29icyAlPiUgCiAgbGVmdF9qb2luKGxhYl9ib3VuZHMsIGJ5ID0gYygnY29uY2VwdF9jb2RlJyA9ICdMT0lOQycpKSAlPiUgCiAgc2VsZWN0KC0gY29uY2VwdF9jb2RlKSAlPiUgCiAgcGl2b3Rfd2lkZXIoaWRfY29scyA9IGMocGF0aWVudF9udW0sIGRheXNfc2luY2VfYWRtaXNzaW9uLCBzZXZlcml0eSksCiAgICAgICAgICAgICAgbmFtZXNfZnJvbSA9IHNob3J0X25hbWUsCiAgICAgICAgICAgICAgdmFsdWVzX2Zyb20gPSB2YWx1ZSwKICAgICAgICAgICAgICB2YWx1ZXNfZm4gPSBtZWFuKQoKI2NoZWNrIE5BcyBpbiB0aGUgV2lkZSBmb3JtYXQKbmFfc3RhdHMgPC0gcGF0aWVudF9vYnNfd2lkZSAlPiUgCiAgc2VsZWN0KC0gYyhwYXRpZW50X251bSwgZGF5c19zaW5jZV9hZG1pc3Npb24sIHNldmVyaXR5KSkgJT4lIAogIGlzLm5hKCkgJT4lIAogIGAhYCAKCm5hX2RmIDwtIGRhdGEuZnJhbWUodmFsdWVfZXhpc3RlZCA9IGNvbFN1bXMobmFfc3RhdHMpLCAKICAgICAgICAgICBwcm9wX2V4aXN0ZWQgPSBjb2xNZWFucyhuYV9zdGF0cykpICU+JSAKICByb3duYW1lc190b19jb2x1bW4oJ2xhYicpICU+JSAKICBtdXRhdGUocHJvcF9uYSA9IDEgLSBwcm9wX2V4aXN0ZWQsCiAgICAgICAgIGxhYiA9IGZjdF9yZW9yZGVyKGxhYiwgdmFsdWVfZXhpc3RlZCkpCgpuX3ZhbHVlcyA8LSBuYV9kZiAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gdmFsdWVfZXhpc3RlZCwgeSA9IGxhYikpICsKICBnZW9tX2NvbCgpICsgCiAgbGFicyh4ID0gJ051bWJlciBvZiB2YWx1ZXMnLCB5ID0gTlVMTCkKCm5hX3Byb2IgPC0gbmFfZGYgJT4lCiAgcmVuYW1lKCdWYWxpZCB2YWx1ZScgPSBwcm9wX2V4aXN0ZWQsICdOQScgPSBwcm9wX25hKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGMoYFZhbGlkIHZhbHVlYCwgYE5BYCkpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSB2YWx1ZSwgeSA9IGxhYiwgZmlsbCA9IG5hbWUpKSArCiAgZ2VvbV9jb2woKSArIAogIHNjYWxlX2ZpbGxfZGlzY3JldGUoZ3VpZGUgPSBndWlkZV9sZWdlbmQocmV2ZXJzZSA9IFRSVUUpKSArCiAgbGFicyh4ID0gJ1Byb3BvcnRpb24nLCB5ID0gTlVMTCkgKwogICMgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQocmV2ZXJzZSA9IFRSVUUpKQogIHRoZW1lKAogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQua2V5LndpZHRoID0gdW5pdCg2LCAnbW0nKSwKICAgIGxlZ2VuZC5rZXkuaGVpZ2h0ID0gdW5pdCg0LCAnbW0nKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICdib3R0b20nKQoKcGxvdF9ncmlkKG5fdmFsdWVzLCBuYV9wcm9iLCBucm93ID0gMSwgYXhpcyA9ICdiJywgYWxpZ24gPSAnaCcpCgpwZW5uX25hX2RmIDwtIG5hX2RmCmBgYAoKIyMgTnVtYmVyIG9mIG9ic2VydmF0aW9uIChkYXlzKSBwZXIgcGF0aWVudApgYGB7Un0KZGF5c19jb3VudF9taW5fbWF4IDwtIHBhdGllbnRfb2JzX3dpZGUgJT4lCiAgZ3JvdXBfYnkocGF0aWVudF9udW0sIHNldmVyaXR5KSAlPiUKICBzdW1tYXJpc2UoCiAgICBuX3ZhbHVlcyA9IG5fZGlzdGluY3QoZGF5c19zaW5jZV9hZG1pc3Npb24pLAogICAgbWluX2RheSA9IG1pbihkYXlzX3NpbmNlX2FkbWlzc2lvbiksCiAgICBtYXhfZGF5ID0gbWF4KGRheXNfc2luY2VfYWRtaXNzaW9uKSwKICAgIC5ncm91cHMgPSAnZHJvcCcKICApICU+JSAKICBtdXRhdGUodGltZV9vYnMgPSBtYXhfZGF5IC0gbWluX2RheSkgJT4lIAogIHNlbGVjdCgtcGF0aWVudF9udW0pICU+JSAKICBhZGRfY291bnQoc2V2ZXJpdHksIG5hbWUgPSAnbl9zZXZlcml0eScpIAoKYWdnX25fdmFsdWVzIDwtIGRheXNfY291bnRfbWluX21heCAlPiUgCiAgY291bnQoc2V2ZXJpdHksIG5fc2V2ZXJpdHksIG5fdmFsdWVzLAogICAgICAgIG5hbWUgPSAnbl9udmFscycpCmFnZ19tYXhfZGF5IDwtIGRheXNfY291bnRfbWluX21heCAlPiUgCiAgY291bnQoc2V2ZXJpdHksIG5fc2V2ZXJpdHksIG1heF9kYXksCiAgICAgICAgbmFtZSA9ICduX21heGRheScpCgoobl9zZXZlcmUgPC0gc3VtKGRheXNfY291bnRfbWluX21heCRzZXZlcml0eSA9PSAnc2V2ZXJlJykpCihuX25vbnNldmVyZSA8LSBzdW0oZGF5c19jb3VudF9taW5fbWF4JHNldmVyaXR5ID09ICdub25zZXZlcmUnKSkKIyBzdW1tYXJ5KGRheXNfY291bnRfbWluX21heCRuX3ZhbHVlcykKYGBgCgojIyBIaXN0b2dyYW0gb2YgdGhlIG51bWJlciBvZiBkYXlzIHdpdGggYXQgbGVhc3Qgb25lIG9ic2VydmF0aW9uCmBgYHtSfQphZ2dfbl92YWx1ZXMgJT4lIAogIGdncGxvdChhZXMoeCA9IG5fdmFsdWVzLCB5ID0gbl9udmFscywgZmlsbCA9IHNldmVyaXR5KSkgKwogIGdlb21fY29sKGFscGhhID0gMC41KSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICdEYXJrMicsIGRpcmVjdGlvbiA9IC0xKSArCiAgbGFicyhmaWxsID0gJ1NldmVyZT8nLCAKICAgICAgIHggPSAiTnVtYmVyIG9mIGRheXMgd2l0aCBkYXRhIiwgCiAgICAgICB5ID0gIkNvdW50IikKYGBgCgojIyBIaXN0b2dyYW0gb2YgbGVuZ3RoIG9mIHN0YXkKaS5lLiBsYXN0IGRheSB3aXRoIG9ic2VydmF0aW9uLWZpcnN0IGRheSB3aXRoIG9ic2VydmF0aW9uCgpXZSBuZWVkIHRvIGNoZWNrIGZvciByZWFkbWlzc2lvbiBoZXJlLgpgYGB7Un0KYWdnX21heF9kYXkgJT4lIAogIGdncGxvdChhZXMoeCA9IG1heF9kYXksIHkgPSBuX21heGRheSwgZmlsbCA9IHNldmVyaXR5KSkgKwogIGdlb21fY29sKGFscGhhID0gMC41KSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICdEYXJrMicsIGRpcmVjdGlvbiA9IC0xKSArCiAgbGFicyhmaWxsID0gJ1NldmVyZT8nLCAKICAgICAgIHggPSAiTnVtYmVyIG9mIGRheXMgd2l0aCBkYXRhIiwgCiAgICAgICB5ID0gIkNvdW50IikKYGBgCgojIyBBbmFseXplIG1pc3NpbmduZXNzIGFuZCBmcmVxdWVuY3kgb2YgbWVhc3VyZXMgZm9yIGVhY2ggbGFiCgpgYGB7cn0KcGF0aWVudF9vYnNfbG9uZyA8LSBwYXRpZW50X29ic193aWRlICU+JSAKICBwaXZvdF9sb25nZXIoLWMocGF0aWVudF9udW0sIGRheXNfc2luY2VfYWRtaXNzaW9uLCBzZXZlcml0eSksCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gJ2xhYicsIHZhbHVlc190byA9ICd2YWx1ZScsCiAgICAgICAgICAgICAgIHZhbHVlc19kcm9wX25hID0gVFJVRSkKICAKcGVyX2xhYiA8LSBwYXRpZW50X29ic19sb25nICU+JSAKICBncm91cF9ieShsYWIsIHBhdGllbnRfbnVtLCBzZXZlcml0eSkgJT4lIAogIGNvdW50KG5hbWUgPSAnbl9vYnMnKSAlPiUgCiAgdW5ncm91cCgpICU+JSAKICBncm91cF9ieShsYWIsIHNldmVyaXR5KSAlPiUgCiAgY291bnQobl9vYnMpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBzZXZlcml0eSwgdmFsdWVzX2Zyb20gPSBuLCB2YWx1ZXNfZmlsbCA9IDApICU+JSAKICBtdXRhdGUoYm90aF9zZXZlcml0aWVzID0gbm9uc2V2ZXJlICsgc2V2ZXJlKSAlPiUgCiAgbXV0YXRlKHByb3Bfbm9uc2V2ZXJlID0gbm9uc2V2ZXJlL25fbm9uc2V2ZXJlLAogICAgICAgICBwcm9wX3NldmVyZSA9IHNldmVyZS9uX3NldmVyZSwKICAgICAgICAgcHJvcF9ib3RoID0gYm90aF9zZXZlcml0aWVzL25yb3coZGF5c19jb3VudF9taW5fbWF4KSkKCmxhYl9tZWRpYW5zIDwtCiAgcGF0aWVudF9vYnNfbG9uZyAlPiUgCiAgYWRkX2NvdW50KGxhYiwgbmFtZSA9ICd0b3RhbF9vYnMnKSAlPiUgCiAgZ3JvdXBfYnkobGFiKSAlPiUgCiAgbXV0YXRlKHRvdGFsX3BhdGllbnRzID0gbGVuZ3RoKHVuaXF1ZShwYXRpZW50X251bSkpKSAlPiUgCiAgYWRkX2NvdW50KHBhdGllbnRfbnVtLCBuYW1lID0gJ25fb2JzX3BhdGllbnRzJykgJT4lIAogIG11dGF0ZShtZWRpYW5fb2JzX3Blcl9wYXRpZW50ID0gbWVkaWFuKG5fb2JzX3BhdGllbnRzKSwKICAgICAgICAgbl9ncmVhdGVyMCA9IG5fb2JzX3BhdGllbnRzID4gbWVkaWFuX29ic19wZXJfcGF0aWVudCwKICAgICAgICAgbl9ncmVhdGVyMSA9IG5fb2JzX3BhdGllbnRzID4gbWVkaWFuX29ic19wZXJfcGF0aWVudCArIDEsCiAgICAgICAgIG5fZ3JlYXRlcjIgPSBuX29ic19wYXRpZW50cyA+IG1lZGlhbl9vYnNfcGVyX3BhdGllbnQgKyAyKSAlPiUgCiAgZ3JvdXBfYnkoc2V2ZXJpdHkpICU+JSAKICBtdXRhdGUoZWFjaF9tZWRfb2JzX3Blcl9wYXRpZW50ID0gbWVkaWFuKG5fb2JzX3BhdGllbnRzKSkgJT4lIAogIHVuZ3JvdXAoc2V2ZXJpdHkpICU+JSAKICBzZWxlY3QoLSBjKGRheXNfc2luY2VfYWRtaXNzaW9uLCB2YWx1ZSkpICU+JSAKICBkaXN0aW5jdCgpICU+JSAKICBncm91cF9ieShsYWIpICU+JSAKICBtdXRhdGUoYWNyb3NzKGNvbnRhaW5zKCduX2dyZWF0ZXInKSwgc3VtKSkgJT4lIAogIHNlbGVjdCgtIGMobl9vYnNfcGF0aWVudHMsIHBhdGllbnRfbnVtKSkgJT4lIAogIGRpc3RpbmN0KCkgJT4lIAogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBzZXZlcml0eSwgdmFsdWVzX2Zyb20gPSBlYWNoX21lZF9vYnNfcGVyX3BhdGllbnQpICU+JSAKICByZW5hbWUoJ21lZGlhbl9vYnNfcGVyX3NldmVyZV9wYXRpZW50JyA9IHNldmVyZSwKICAgICAgICAgJ21lZGlhbl9vYnNfcGVyX25vbl9zZXZlcmVfcGF0aWVudCcgPSBub25zZXZlcmUpICU+JSAKICBzZWxlY3QobGFiLCB0b3RhbF9vYnMsIHRvdGFsX3BhdGllbnRzLCBzdGFydHNfd2l0aCgnbWVkJyksIHN0YXJ0c193aXRoKCduXycpKQoKbGFiX21lZGlhbnMgJT4lIAogIGRhdGF0YWJsZShyb3duYW1lcyA9IEZBTFNFKQpgYGAKCgpgbl9ncmVhdGVyMGAgc2hvd3MgdGhlIG51bWJlciBvZiBwYXRpZW50cyB3aG8gaGFkIG1vcmUgb2JzZXJ2YXRpb25zIHRoYW4gdGhlIG1lZGlhbi4KYG5fZ3JlYXRlcjFgIHNob3dzIHRoZSBudW1iZXIgb2YgcGF0aWVudHMgd2hvIGhhZCBtb3JlIG9ic2VydmF0aW9ucyB0aGFuIHRoZSBtZWRpYW4gKyAxLgpgbl9ncmVhdGVyMmAgc2hvd3MgdGhlIG51bWJlciBvZiBwYXRpZW50cyB3aG8gaGFkIG1vcmUgb2JzZXJ2YXRpb25zIHRoYW4gdGhlIG1lZGlhbiArIDIuCgpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQpsYWJfbWVkaWFucyAlPiUgCiAgc2VsZWN0KGxhYiwgdG90YWxfb2JzLCB0b3RhbF9wYXRpZW50cykgJT4lCiAgcGl2b3RfbG9uZ2VyKC0gbGFiKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gdmFsdWUsIHkgPSBmY3RfcmVvcmRlcihsYWIsIHZhbHVlKSkpICsKICBnZW9tX2NvbCgpICsKICBmYWNldF9ncmlkKGNvbHMgPSB2YXJzKG5hbWUpLCBzY2FsZXMgPSAnZnJlZV94Jywgc3BhY2UgPSAnZnJlZScpICsKICBsYWJzKHkgPSBOVUxMKQpgYGAKCkluIHRoZSBmaWd1cmUgYmVsb3c6CgotIEdyZXkgZGFzaCBsaW5lOiBgUmVmZXJlbmNlIExvd2AKLSBHcmV5IHNvbGlkIGxpbmU6IGBSZWZlcmVuY2UgSGlnaGAKLSBCbGFjayBkYXNoIGxpbmU6IGBsb3dlciBib3VuZCBvdXRsaWVyYCAoUUMpCi0gQmxhY2sgc29saWQgbGluZTogYHVwcGVyIGJvdW5kIG91dGxpZXJgIChRQykKCmBgYHtyIGZpZy53aWR0aD05LCBmaWcuaGVpZ2h0PTExfQpwYXRpZW50X29ic19sb25nICU+JSAKICBsZWZ0X2pvaW4obGFiX2JvdW5kcywgYnkgPSBjKCdsYWInID0gJ3Nob3J0X25hbWUnKSkgJT4lIAogIGdncGxvdChhZXMoeSA9IHNldmVyaXR5LCB4ID0gdmFsdWUsIGZpbGwgPSBzZXZlcml0eSkpICsKICBnZW9tX3Zpb2xpbigpICsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gJ0RhcmsyJywgZ3VpZGUgPSBndWlkZV9sZWdlbmQocmV2ZXJzZSA9IFRSVUUpKSArCiAgbGFicyh5ID0gTlVMTCwgeCA9IE5VTEwpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gYFJlZmVyZW5jZSBMb3dgKSwgbGluZXR5cGUgPSAnZGFzaGVkJywgY29sb3IgPSAnZ3JleScpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gYFJlZmVyZW5jZSBIaWdoYCksIGNvbG9yID0gJ2dyZXknKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IExCKSwgbGluZXR5cGUgPSAnZGFzaGVkJykgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBVQikpICsKICBmYWNldF93cmFwKH4gbGFiLCBzY2FsZXMgPSAnZnJlZScsIG5jb2wgPSAyLCBzdHJpcC5wb3NpdGlvbiA9ICdsZWZ0JykgKwogIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCiMjIE1pc3NpbmcgZGF0YSBoZWF0bWFwCgpDYXBwZWQgYXQgMjAgZGF5cyB3aXRoIG9ic2VydmF0aW9ucy4KCmBgYHtyIGZpZy53aWR0aD0xMn0KcGVyX2xhYiAlPiUgCiAgc2VsZWN0KGxhYiwgbl9vYnMsIGJvdGhfc2V2ZXJpdGllcywgc2V2ZXJlLCBub25zZXZlcmUpICU+JSAKICBmaWx0ZXIobl9vYnMgPD0gMjApICU+JSAKICBwaXZvdF9sb25nZXIoYyhib3RoX3NldmVyaXRpZXMsIHNldmVyZSwgbm9uc2V2ZXJlKSkgJT4lIAogIG11dGF0ZShuYW1lID0gbmFtZSAlPiUgZmN0X3JlY29kZSgKICAgICdBbGwgcGF0aWVudHMnID0gJ2JvdGhfc2V2ZXJpdGllcycsCiAgICAnU2V2ZXJlIHBhdGllbnRzJyA9ICdzZXZlcmUnLAogICAgJ05vbi1zZXZlcmUgcGF0aWVudHMnID0gJ25vbnNldmVyZScKICApKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gbl9vYnMsIGZpbGwgPSB2YWx1ZSwgeSA9IGZjdF9yZW9yZGVyKGxhYiwgbl9vYnMpKSkgKyAKICBnZW9tX3RpbGUoY29sb3VyID0gIndoaXRlIiwgc2l6ZSA9IDAuMikrCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHZhbHVlKSwgY29sb3VyID0gIndoaXRlIiwgc2l6ZSA9IDIpICsKICBzY2FsZV95X2Rpc2NyZXRlKGV4cGFuZCA9IGMoMCwgMCkpKwogIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBjKDAsIDApLAogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKDE6bWF4KHBlcl9sYWIkbl9vYnMpKSwKICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygxOm1heChwZXJfbGFiJG5fb2JzKSkpKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gImxpZ2h0Z3JleSIsIGhpZ2ggPSAiZGFya2JsdWUiLAogICAgICAgICAgICAgICAgICAgICAgbGltaXRzID0gYygwLCBtYXgocGVyX2xhYiRib3RoX3NldmVyaXRpZXMpKSkgKwogIGZhY2V0X3dyYXAofiBuYW1lLCBucm93ID0gMSkgKwogIGxhYnMoeCA9ICdOdW1iZXIgb2YgdmFsdWVzIGEgcGF0aWVudCBoYXMgZm9yIGVhY2ggbGFiJywKICAgICAgIHkgPSBOVUxMLCBmaWxsID0gJyMgcGF0aWVudHMnKSArCiAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSBjKDAuOTMsIDAuMiksCiAgICAgICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpCiAgKQpgYGAKCiJCaW5uZWQiIGhlYXRtYXA6IGV2ZXJ5IDE1IGRheXMKCmBgYHtyIGZpZy53aWR0aD0xMn0KcGVyX2xhYiAlPiUKICBtdXRhdGUob2JzX2JpbiA9IGN1dChuX29icywgYnJlYWtzID0gMTUsIGluY2x1ZGUubG93ZXN0ID0gVFJVRSkpICU+JSAKICBncm91cF9ieShsYWIsIG9ic19iaW4pICU+JSAKICBzdW1tYXJpc2UoYm90aF9zZXZlcml0aWVzID0gc3VtKGJvdGhfc2V2ZXJpdGllcyksIAogICAgICAgICAgICBzZXZlcmUgPSBzdW0oc2V2ZXJlKSwgCiAgICAgICAgICAgIG5vbnNldmVyZSA9IHN1bShub25zZXZlcmUpLCAKICAgICAgICAgICAgLmdyb3VwcyA9ICdkcm9wJykgJT4lIAogIHNlbGVjdChsYWIsIG9ic19iaW4sIGJvdGhfc2V2ZXJpdGllcywgc2V2ZXJlLCBub25zZXZlcmUpICU+JSAKICBwaXZvdF9sb25nZXIoYyhib3RoX3NldmVyaXRpZXMsIHNldmVyZSwgbm9uc2V2ZXJlKSkgJT4lIAogIG11dGF0ZShuYW1lID0gbmFtZSAlPiUgZmN0X3JlY29kZSgKICAgICdBbGwgcGF0aWVudHMnID0gJ2JvdGhfc2V2ZXJpdGllcycsCiAgICAnU2V2ZXJlIHBhdGllbnRzJyA9ICdzZXZlcmUnLAogICAgJ05vbi1zZXZlcmUgcGF0aWVudHMnID0gJ25vbnNldmVyZScKICApKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gb2JzX2JpbiwgZmlsbCA9IHZhbHVlLCB5ID0gZmN0X3Jlb3JkZXIobGFiLCB2YWx1ZSkpKSArIAogIGdlb21fdGlsZShjb2xvdXIgPSAid2hpdGUiLCBzaXplID0gMC4yKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHZhbHVlKSwgY29sb3VyID0gIndoaXRlIiwgc2l6ZSA9IDIpICsKICBzY2FsZV95X2Rpc2NyZXRlKGV4cGFuZCA9IGMoMCwgMCkpKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gImxpZ2h0Z3JleSIsIGhpZ2ggPSAiZGFya2JsdWUiKSArCiAgZmFjZXRfd3JhcCh+IG5hbWUsIG5yb3cgPSAxKSArCiAgbGFicyh4ID0gJ051bWJlciBvZiB2YWx1ZXMgYSBwYXRpZW50IGhhcyBmb3IgZWFjaCBsYWInLAogICAgICAgeSA9IE5VTEwsIGZpbGwgPSAnIyBwYXRpZW50cycpICsKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC45MywgMC4yKSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpLAogICAgICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKQogICkKYGBgCgpgYGB7cn0KYm94cGxvdChwZXJfbGFiJG5fb2JzKQpzdW0ocGVyX2xhYiRuX29icyA+IDkwKQpgYGAKCmBgYHtyIGZpZy53aWR0aD0xMn0KcGVyX2xhYiAlPiUgCiAgc2VsZWN0KGxhYiwgbl9vYnMsIHByb3BfYm90aCwgcHJvcF9zZXZlcmUsIHByb3Bfbm9uc2V2ZXJlKSAlPiUgCiAgZmlsdGVyKG5fb2JzIDw9IDIwKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGMocHJvcF9ib3RoLCBwcm9wX3NldmVyZSwgcHJvcF9ub25zZXZlcmUpKSAlPiUgCiAgbXV0YXRlKG5hbWUgPSBuYW1lICU+JSBmY3RfcmVjb2RlKAogICAgJ0NvbXBhcmVkIHRvIGFsbCBwYXRpZW50cycgPSAncHJvcF9ib3RoJywKICAgICdDb21wYXJlZCB0byBhbGwgc2V2ZXJlIHBhdGllbnRzJyA9ICdwcm9wX3NldmVyZScsCiAgICAnQ29tcGFyZWQgdG8gYWxsIG5vbi1zZXZlcmUgcGF0aWVudHMnID0gJ3Byb3Bfbm9uc2V2ZXJlJwogICkpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBuX29icywgZmlsbCA9IHZhbHVlLCB5ID0gZmN0X3Jlb3JkZXIobGFiLCBuX29icykpKSArIAogIGdlb21fdGlsZShjb2xvdXIgPSAid2hpdGUiLCBzaXplID0gMC4yKSsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcm91bmQodmFsdWUsIDIpKSwgY29sb3VyID0gIndoaXRlIiwgc2l6ZSA9IDIpICsKICBzY2FsZV95X2Rpc2NyZXRlKGV4cGFuZCA9IGMoMCwgMCkpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLCAwKSwKICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygxOm1heChwZXJfbGFiJG5fb2JzKSksCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoMTptYXgocGVyX2xhYiRuX29icykpKSsgCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAibGlnaHRncmV5IiwgaGlnaCA9ICJkYXJrYmx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnRfZm9ybWF0KGFjY3VyYWN5ID0gMUwpKSArCiAgZmFjZXRfd3JhcCh+IG5hbWUsIG5yb3cgPSAxKSArCiAgbGFicyh4ID0gJ051bWJlciBvZiB2YWx1ZXMgYSBwYXRpZW50IGhhcyBmb3IgZWFjaCBsYWInLAogICAgICAgeSA9IE5VTEwsIGZpbGwgPSAnJSBwYXRpZW50cycpICsKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC45MywgMC4yKSwKICAgICAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCkKICApCmBgYAoKYGBge3IgZmlnLndpZHRoPTEyfQpwZXJfbGFiICU+JQogIG11dGF0ZShvYnNfYmluID0gY3V0KG5fb2JzLCBicmVha3MgPSAxNSwgaW5jbHVkZS5sb3dlc3QgPSBUUlVFKSkgJT4lIAogIGdyb3VwX2J5KGxhYiwgb2JzX2JpbikgJT4lIAogIHN1bW1hcmlzZShwcm9wX2JvdGggPSBzdW0ocHJvcF9ib3RoKSwgCiAgICAgICAgICAgIHByb3Bfc2V2ZXJlID0gc3VtKHByb3Bfc2V2ZXJlKSwgCiAgICAgICAgICAgIHByb3Bfbm9uc2V2ZXJlID0gc3VtKHByb3Bfbm9uc2V2ZXJlKSwgCiAgICAgICAgICAgIC5ncm91cHMgPSAnZHJvcCcpICU+JSAKICBzZWxlY3QobGFiLCBvYnNfYmluLCBwcm9wX2JvdGgsIHByb3Bfc2V2ZXJlLCBwcm9wX25vbnNldmVyZSkgJT4lIAogIHBpdm90X2xvbmdlcihjKHByb3BfYm90aCwgcHJvcF9zZXZlcmUsIHByb3Bfbm9uc2V2ZXJlKSkgJT4lIAogIG11dGF0ZShuYW1lID0gbmFtZSAlPiUgZmN0X3JlY29kZSgKICAgICdDb21wYXJlZCB0byBhbGwgcGF0aWVudHMnID0gJ3Byb3BfYm90aCcsCiAgICAnQ29tcGFyZWQgdG8gYWxsIHNldmVyZSBwYXRpZW50cycgPSAncHJvcF9zZXZlcmUnLAogICAgJ0NvbXBhcmVkIHRvIGFsbCBub24tc2V2ZXJlIHBhdGllbnRzJyA9ICdwcm9wX25vbnNldmVyZScKICApKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gb2JzX2JpbiwgZmlsbCA9IHZhbHVlLCB5ID0gZmN0X3Jlb3JkZXIobGFiLCB2YWx1ZSkpKSArIAogIGdlb21fdGlsZShjb2xvdXIgPSAid2hpdGUiLCBzaXplID0gMC4yKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHJvdW5kKHZhbHVlLCAyKSksIGNvbG91ciA9ICJ3aGl0ZSIsIHNpemUgPSAyKSArCiAgc2NhbGVfeV9kaXNjcmV0ZShleHBhbmQgPSBjKDAsIDApKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAibGlnaHRncmV5IiwgaGlnaCA9ICJkYXJrYmx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnRfZm9ybWF0KGFjY3VyYWN5ID0gMUwpKSArCiAgZmFjZXRfd3JhcCh+IG5hbWUsIG5yb3cgPSAxKSArCiAgbGFicyh4ID0gJ051bWJlciBvZiB2YWx1ZXMgYSBwYXRpZW50IGhhcyBmb3IgZWFjaCBsYWInLAogICAgICAgeSA9IE5VTEwsIGZpbGwgPSAnJSBwYXRpZW50cycpICsKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC45MywgMC4yKSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpLAogICAgICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKQogICkKYGBgCgpEZW5vbWluYXRvcjogdG90YWwgbnVtYmVyIG9mIHBhdGllbnRzLCB0b3RhbCBudW1iZXIgb2Ygbm9uLXNldmVyZSBwYXRpZW50cywKYW5kIHRvdGFsIG51bWJlciBvZiBzZXZlcmUgcGF0aWVudHMsIHJlc3BlY3RpdmVseS4KCmBgYHtyfQpwZXJfbGFiICU+JSAKICBzZWxlY3QobGFiLCBuX29icywgc2V2ZXJlLCBub25zZXZlcmUpICU+JSAKICBmaWx0ZXIobl9vYnMgPD0gOTApICU+JQogIHBpdm90X2xvbmdlcihjKHNldmVyZSwgbm9uc2V2ZXJlKSkgJT4lIAogIG11dGF0ZShsYWIgPSBmY3RfcmVvcmRlcihsYWIsIG5fb2JzKSwKICAgICAgICAgbmFtZSA9IG5hbWUgJT4lIGZjdF9yZWNvZGUoCiAgICAnU2V2ZXJlIHBhdGllbnRzJyA9ICdzZXZlcmUnLAogICAgJ05vbi1zZXZlcmUgcGF0aWVudHMnID0gJ25vbnNldmVyZScKICApKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gbl9vYnMsIGZpbGwgPSBuYW1lLCB5ID0gdmFsdWUpKSArIAogIGdlb21fY29sKCkgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAnRGFyazInLCBkaXJlY3Rpb24gPSAtMSkgKwogIGZhY2V0X3dyYXAofiBsYWIsIHNjYWxlcyA9ICdmcmVlJykgKwogIGxhYnMoeCA9ICdOdW1iZXIgb2YgdmFsdWVzIGEgcGF0aWVudCBoYXMgZm9yIGVhY2ggbGFiJywKICAgICAgIHkgPSBOVUxMLCBmaWxsID0gJyMgcGF0aWVudHMnKSArCiAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSBjKDAuOSwgMC4yKSwKICAgICAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCkKICApCmBgYAoKCmBgYHtyfQpzZXNzaW9uSW5mbygpCmBgYAo=